#ifndef NETWORK_EVENTS_H
#define NETWORK_EVENTS_H

#include "Network.h"

// Purpose: Defines base classes for network events.
// Status:  Untested but should be usable.
// Usage:   To properly create a custom network event, you must derive it from 
//          RegisteredClientEvent< MyCustomEvent > or
//          RegisteredServerEvent< MyCustomEvent > (using the CRTP). This will
//          cause the event to self-register with the Multiplayer instance. You 
//          may then use the event as you like.

//A game event that occurs on the client
class ClientEvent
{
	//Allow Multiplayer to access the send( unsigned char iPacketID ) function.
	//TODO: Check if we really do\might need to use ClientEvent::send(unsigned
	//      char) explicitly.
	//friend class Multiplayer;
public:
    
    //Call this locally to apply the action of the event and also send it
    // across the network
    void activate()
    {
		if ( !Multiplayer::get()->getClient() ) {
			std::cerr << "Error in ClientEvent::activate(): Trying to activate \
client event on dedicated server. Bailing." << std::endl;
			return;
		}
		
        onActivate();       //Activate the event locally
        send();             //Send the event over the network
    }
    
    //Call this in response to a received packet corresponding to this event
    void respond(RakNet::BitStream& bsData)
    {
        decode(bsData);         //Retrieve the event's data from the packet
        //onRespond();            //Respond to the remote event
        onActivate();
    }
    
    //Writes the event to a bitstream and sends it over the network
    //NOTE: This 'version' of send could be called by Multiplayer
    //void send( unsigned char iPacketID );
    void send();
    
protected:
    
    //Constructor sets default values for events
    ClientEvent() : /*m_pPlayer(0), */m_iPriority(HIGH_PRIORITY), m_iReliability(RELIABLE_ORDERED)       {}
    ClientEvent(PlayerID ID) : m_iPlayerID(ID), m_iPriority(HIGH_PRIORITY), m_iReliability(RELIABLE_ORDERED)       {}
    
    //Return a unique packet identifier for this event
    //virtual unsigned char getPacketID() = 0;
    
    //This is overriden by RegisteredClientEvent.
    //DO NOT OVERRIDE THIS YOURSELF! (unless you want to use something other
    // than RegisteredClientEvent).
    virtual int getPacketID() = 0;
    
    //Virtual methods to be defined in real event classes
    //---------------------------------------------------
    //Called when the event is activated locally
    virtual void onActivate() = 0;
    
    //Called when responding to the received remote event
    //virtual void onRespond() = 0;
    
    //Override to write all the event's data to a bitstream and return it
    virtual void encode(RakNet::BitStream& bsEvent) = 0;
    
    //Override to read all the event's data from the given bitstream
    virtual void decode(RakNet::BitStream& bsData) = 0;
    //---------------------------------------------------
    
    PacketPriority    m_iPriority;
    PacketReliability m_iReliability;
    
    PlayerID          m_iPlayerID;
};

//This class uses compile-time instantiation of templates along with a static
// member variable to self-register itself with the global Multiplayer object.
template< class T >
class RegisteredClientEvent : public ClientEvent
{
protected:
	//This class can only be a superclass
	RegisteredClientEvent() {}
	
	//Returns the generated packet identifier for this event type
	virtual int getPacketID()
	{
		//Check if event self-registration failed
		if ( -1 == sm_iPacketID )
			return -1;
		else
			return ID_USER_PACKET_ENUM + sm_iPacketID;
	}
	
private:
	//The event's self-registering packet identifier
	static int sm_iPacketID;
};

//This is where the event is self-registered.
template< class T >
int RegisteredClientEvent<T>::sm_iPacketID = Multiplayer::get()->registerClientEvent< T >();

//---------------------------------------------------------------------

//A game event that occurs on the server
class ServerEvent
{

public:
    
    //Call locally to apply the action of the event and also send it across the network,
    //possibly to a specific player.
    void activate()
    {
		if ( !Multiplayer::get()->getServer() ) {
			std::cerr << "Error in ServerEvent::activate(): Trying to activate \
server event on non-hosting client. Bailing." << std::endl;
			return;
		}
		
        onActivate();       //Activate the event locally
        //TODO: Get the global Multiplayer instance here properly!
        if (Multiplayer::get()->isServer())
            send(m_pidTarget);             //Send the event over the network
    }
    
    //Call in response to a received packet corresponding to this event
    void respond(RakNet::BitStream& bsData)
    {
        decode(bsData);         //Retrieve the event's data from the packet
        //onRespond();            //Respond to the remote event
        onActivate();
    }
    
    //Writes the event to a bitstream and sends it over the network
    void send( PlayerID pidPlayer = UNASSIGNED_PLAYER_ID );
    
protected:
    
    //Constructor sets default values for events
    ServerEvent() : m_pidTarget(UNASSIGNED_PLAYER_ID), m_iPriority(HIGH_PRIORITY), m_iReliability(RELIABLE_ORDERED)       {}
    ServerEvent(PlayerID pidTarget) : m_pidTarget(pidTarget), m_iPriority(HIGH_PRIORITY), m_iReliability(RELIABLE_ORDERED)       {}
    
    //This is overriden by RegisteredServerEvent.
    //DO NOT OVERRIDE THIS YOURSELF! (unless you want to use something other
    // than RegisteredServerEvent).
    virtual int getPacketID() = 0;
    
    //Virtual methods to be defined in real event classes
    //---------------------------------------------------
    //Called when the event is activated locally
    virtual void onActivate() = 0;
    
    //Called when responding to the received remote event
    //virtual void onRespond() = 0;
    
    //Override to write all the event's data to a bitstream and return it
    virtual void encode(RakNet::BitStream& bsEvent) = 0;
    
    //Override to read all the event's data from the given bitstream
    virtual void decode(RakNet::BitStream& bsData) = 0;
    //---------------------------------------------------
    
    //TODO: Think about if need more parameters for who to send to, ie. all clients, all but one, one only
    PlayerID 			 m_pidTarget;		//UNASSIGNED_PLAYER_ID => all players
    PacketPriority       m_iPriority;
    PacketReliability    m_iReliability;
};

//This class uses compile-time instantiation of templates along with a static
// member variable to self-register itself with the global Multiplayer object.
template< class T >
class RegisteredServerEvent : public ServerEvent
{
protected:
	//This class can only be a superclass
	RegisteredServerEvent() {}
	
	//Returns the generated packet identifier for this event type
	virtual int getPacketID()
	{
		//Check if event self-registration failed
		if ( -1 == sm_iPacketID )
			return -1;
		else
			return ID_USER_PACKET_ENUM + sm_iPacketID;
	}
	
private:
	//The event's self-registering packet identifier
	static int sm_iPacketID;
};

//This is where the event is self-registered.
template< class T >
int RegisteredServerEvent<T>::sm_iPacketID = Multiplayer::get()->registerServerEvent< T >();

#endif //NETWORK_EVENTS_H
